<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/css/all.min.css" integrity="sha256-mUZM63G8m73Mcidfrv5E+Y61y7a12O5mW4ezU3bxqW4=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"tallgy.gitee.io","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>
<meta name="description" content="React 高级指引无障碍语义化的 HTML​        简单来说，就是如果对于比如 dl dt dd，其中这样的构造。 ​        正常的使用 ，就是如同下面这样，因为return需要一个根组件，但是我们有两个标签，所以需要将其用div包含起来。 ​        但是这样就会破坏语义性。所以就需要使用一个 Fragment 标签，其语义和template标签可以说是一样的。 1234">
<meta property="og:type" content="article">
<meta property="og:title" content="react-高级指引">
<meta property="og:url" content="http://tallgy.gitee.io/2022/02/17/React/react-%E9%AB%98%E7%BA%A7%E6%8C%87%E5%BC%95/index.html">
<meta property="og:site_name" content="tallgy&#39;s blog">
<meta property="og:description" content="React 高级指引无障碍语义化的 HTML​        简单来说，就是如果对于比如 dl dt dd，其中这样的构造。 ​        正常的使用 ，就是如同下面这样，因为return需要一个根组件，但是我们有两个标签，所以需要将其用div包含起来。 ​        但是这样就会破坏语义性。所以就需要使用一个 Fragment 标签，其语义和template标签可以说是一样的。 1234">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://tallgy.gitee.io/.io//image-20220217140334480.png">
<meta property="og:image" content="http://tallgy.gitee.io/.io//should-component-update.png">
<meta property="article:published_time" content="2022-02-17T03:00:05.000Z">
<meta property="article:modified_time" content="2023-06-25T08:57:29.323Z">
<meta property="article:author" content="tallgy">
<meta property="article:tag" content="React">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://tallgy.gitee.io/.io//image-20220217140334480.png">


<link rel="canonical" href="http://tallgy.gitee.io/2022/02/17/React/react-%E9%AB%98%E7%BA%A7%E6%8C%87%E5%BC%95/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"http://tallgy.gitee.io/2022/02/17/React/react-%E9%AB%98%E7%BA%A7%E6%8C%87%E5%BC%95/","path":"2022/02/17/React/react-高级指引/","title":"react-高级指引"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>react-高级指引 | tallgy's blog</title>
  




  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">tallgy's blog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>







</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#React-%E9%AB%98%E7%BA%A7%E6%8C%87%E5%BC%95"><span class="nav-number">1.</span> <span class="nav-text">React 高级指引</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%97%A0%E9%9A%9C%E7%A2%8D"><span class="nav-number">1.1.</span> <span class="nav-text">无障碍</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AF%AD%E4%B9%89%E5%8C%96%E7%9A%84-HTML"><span class="nav-number">1.1.1.</span> <span class="nav-text">语义化的 HTML</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%8E%A7%E5%88%B6%E7%84%A6%E7%82%B9"><span class="nav-number">1.1.2.</span> <span class="nav-text">控制焦点</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BD%BF%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%AE%A1%E7%90%86%E7%84%A6%E7%82%B9"><span class="nav-number">1.1.3.</span> <span class="nav-text">使用程序管理焦点</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%BC%80%E5%8F%91%E8%BE%85%E5%8A%A9"><span class="nav-number">1.1.4.</span> <span class="nav-text">开发辅助</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#eslint-plugin-jsx-a11y"><span class="nav-number">1.1.4.1.</span> <span class="nav-text">eslint-plugin-jsx-a11y</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link"><span class="nav-number">1.1.5.</span> <span class="nav-text"></span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BB%A3%E7%A0%81%E5%88%86%E5%89%B2"><span class="nav-number">1.2.</span> <span class="nav-text">代码分割</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#import"><span class="nav-number">1.2.1.</span> <span class="nav-text">import()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#React-lazy"><span class="nav-number">1.2.2.</span> <span class="nav-text">React.lazy</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%BC%82%E5%B8%B8%E6%8D%95%E8%8E%B7%E8%BE%B9%E7%95%8C"><span class="nav-number">1.2.3.</span> <span class="nav-text">异常捕获边界</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9F%BA%E4%BA%8E%E8%B7%AF%E7%94%B1%E7%9A%84%E4%BB%A3%E7%A0%81%E5%88%86%E5%89%B2"><span class="nav-number">1.2.4.</span> <span class="nav-text">基于路由的代码分割</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%91%BD%E5%90%8D%E5%AF%BC%E5%87%BA"><span class="nav-number">1.2.5.</span> <span class="nav-text">命名导出</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Context"><span class="nav-number">1.3.</span> <span class="nav-text">Context</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#React-createContext-API"><span class="nav-number">1.3.1.</span> <span class="nav-text">React.createContext API</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%94%99%E8%AF%AF%E8%BE%B9%E7%95%8C"><span class="nav-number">1.4.</span> <span class="nav-text">错误边界</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%9C%AA%E6%8D%95%E8%8E%B7%E9%94%99%E8%AF%AF%E7%9A%84%E6%96%B0%E8%A1%8C%E4%B8%BA"><span class="nav-number">1.4.1.</span> <span class="nav-text">未捕获错误的新行为</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BB%84%E4%BB%B6%E6%A0%88%E8%BF%BD%E8%B8%AA"><span class="nav-number">1.4.2.</span> <span class="nav-text">组件栈追踪</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#refs%E8%BD%AC%E5%8F%91"><span class="nav-number">1.5.</span> <span class="nav-text">refs转发</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Fragments"><span class="nav-number">1.6.</span> <span class="nav-text">Fragments</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%AB%98%E9%98%B6%E7%BB%84%E4%BB%B6"><span class="nav-number">1.7.</span> <span class="nav-text">高阶组件</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%B7%B1%E5%85%A5JSX"><span class="nav-number">1.8.</span> <span class="nav-text">深入JSX</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%8C%87%E5%AE%9A-React-%E5%85%83%E7%B4%A0%E7%B1%BB%E5%9E%8B"><span class="nav-number">1.8.1.</span> <span class="nav-text">指定 React 元素类型</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#React-%E5%BF%85%E9%A1%BB%E5%9C%A8%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%86%85"><span class="nav-number">1.8.1.1.</span> <span class="nav-text">React 必须在作用域内</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%9C%A8-JSX-%E7%B1%BB%E5%9E%8B%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%82%B9%E8%AF%AD%E6%B3%95"><span class="nav-number">1.8.1.2.</span> <span class="nav-text">在 JSX 类型中使用点语法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%94%A8%E6%88%B7%E5%AE%9A%E4%B9%89%E7%9A%84%E7%BB%84%E4%BB%B6%E5%BF%85%E9%A1%BB%E4%BB%A5%E5%A4%A7%E5%86%99%E5%AD%97%E6%AF%8D%E5%BC%80%E5%A4%B4"><span class="nav-number">1.8.1.3.</span> <span class="nav-text">用户定义的组件必须以大写字母开头</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%9C%A8%E8%BF%90%E8%A1%8C%E6%97%B6%E9%80%89%E6%8B%A9%E7%B1%BB%E5%9E%8B"><span class="nav-number">1.8.1.4.</span> <span class="nav-text">在运行时选择类型</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#JSX-%E4%B8%AD%E7%9A%84-Props"><span class="nav-number">1.8.2.</span> <span class="nav-text">JSX 中的 Props</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E9%87%8F"><span class="nav-number">1.8.2.1.</span> <span class="nav-text">字符串字面量</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Props-%E9%BB%98%E8%AE%A4%E5%80%BC%E4%B8%BA-%E2%80%9CTrue%E2%80%9D"><span class="nav-number">1.8.2.2.</span> <span class="nav-text">Props 默认值为 “True”</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%B1%9E%E6%80%A7%E5%B1%95%E5%BC%80"><span class="nav-number">1.8.2.3.</span> <span class="nav-text">属性展开</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#JSX-%E5%AD%90%E5%85%83%E7%B4%A0"><span class="nav-number">1.8.2.4.</span> <span class="nav-text">JSX 子元素</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%87%BD%E6%95%B0%E4%BD%9C%E4%B8%BA%E5%AD%90%E5%85%83%E7%B4%A0"><span class="nav-number">1.8.2.5.</span> <span class="nav-text">函数作为子元素</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%B8%83%E5%B0%94%E7%B1%BB%E5%9E%8B%E3%80%81Null-%E4%BB%A5%E5%8F%8A-Undefined-%E5%B0%86%E4%BC%9A%E5%BF%BD%E7%95%A5"><span class="nav-number">1.8.2.6.</span> <span class="nav-text">布尔类型、Null 以及 Undefined 将会忽略</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96"><span class="nav-number">1.9.</span> <span class="nav-text">性能优化</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%99%9A%E6%8B%9F%E5%8C%96%E9%95%BF%E5%88%97%E8%A1%A8"><span class="nav-number">1.9.1.</span> <span class="nav-text">虚拟化长列表</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%81%BF%E5%85%8D%E8%B0%83%E5%81%9C"><span class="nav-number">1.9.2.</span> <span class="nav-text">避免调停</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#shouldComponentUpdate-%E7%9A%84%E4%BD%9C%E7%94%A8"><span class="nav-number">1.9.3.</span> <span class="nav-text">shouldComponentUpdate 的作用</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%A4%BA%E4%BE%8B"><span class="nav-number">1.9.3.1.</span> <span class="nav-text">示例</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Portals"><span class="nav-number">1.10.</span> <span class="nav-text">Portals</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Profiler-API"><span class="nav-number">1.11.</span> <span class="nav-text">Profiler API</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8D%8F%E8%B0%83"><span class="nav-number">1.12.</span> <span class="nav-text">协调</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AE%BE%E8%AE%A1%E5%8A%A8%E6%9C%BA"><span class="nav-number">1.12.1.</span> <span class="nav-text">设计动机</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#diffing-%E7%AE%97%E6%B3%95"><span class="nav-number">1.12.2.</span> <span class="nav-text">diffing 算法</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%AF%B9%E6%AF%94%E4%B8%8D%E5%90%8C%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%85%83%E7%B4%A0"><span class="nav-number">1.12.2.1.</span> <span class="nav-text">对比不同类型的元素</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%AF%B9%E6%AF%94%E5%90%8C%E4%B8%80%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%85%83%E7%B4%A0"><span class="nav-number">1.12.2.2.</span> <span class="nav-text">对比同一类型的元素</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%AF%B9%E6%AF%94%E5%90%8C%E7%B1%BB%E5%9E%8B%E7%9A%84%E7%BB%84%E4%BB%B6%E5%85%83%E7%B4%A0"><span class="nav-number">1.12.2.3.</span> <span class="nav-text">对比同类型的组件元素</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%AF%B9%E5%AD%90%E8%8A%82%E7%82%B9%E8%BF%9B%E8%A1%8C%E9%80%92%E5%BD%92"><span class="nav-number">1.12.2.4.</span> <span class="nav-text">对子节点进行递归</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Keys"><span class="nav-number">1.12.2.5.</span> <span class="nav-text">Keys</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%9D%83%E8%A1%A1"><span class="nav-number">1.12.2.6.</span> <span class="nav-text">权衡</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Refs-and-the-DOM"><span class="nav-number">1.13.</span> <span class="nav-text">Refs and the DOM</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9B%9E%E8%B0%83Ref%E5%92%8C%E6%99%AE%E9%80%9Aref%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="nav-number">1.13.1.</span> <span class="nav-text">回调Ref和普通ref的使用</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%99%AE%E9%80%9Aref"><span class="nav-number">1.13.1.1.</span> <span class="nav-text">普通ref</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%9B%9E%E8%B0%83ref"><span class="nav-number">1.13.1.2.</span> <span class="nav-text">回调ref</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%85%B3%E4%BA%8E%E5%9B%9E%E8%B0%83-refs-%E7%9A%84%E8%AF%B4%E6%98%8E"><span class="nav-number">1.13.1.3.</span> <span class="nav-text">关于回调 refs 的说明</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Render-Props"><span class="nav-number">1.14.</span> <span class="nav-text">Render Props</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BD%BF%E7%94%A8-Render-Props-%E6%9D%A5%E8%A7%A3%E5%86%B3%E6%A8%AA%E5%88%87%E5%85%B3%E6%B3%A8%E7%82%B9%EF%BC%88Cross-Cutting-Concerns%EF%BC%89"><span class="nav-number">1.14.1.</span> <span class="nav-text">使用 Render Props 来解决横切关注点（Cross-Cutting Concerns）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9"><span class="nav-number">1.14.2.</span> <span class="nav-text">注意事项</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%B0%86-Render-Props-%E4%B8%8E-React-PureComponent-%E4%B8%80%E8%B5%B7%E4%BD%BF%E7%94%A8%E6%97%B6%E8%A6%81%E5%B0%8F%E5%BF%83"><span class="nav-number">1.14.2.1.</span> <span class="nav-text">将 Render Props 与 React.PureComponent 一起使用时要小心</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%9D%99%E6%80%81%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%9F%A5"><span class="nav-number">1.15.</span> <span class="nav-text">静态类型检查</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B7%BB%E5%8A%A0-TypeScript-%E5%88%B0%E7%8E%B0%E6%9C%89%E9%A1%B9%E7%9B%AE%E4%B8%AD"><span class="nav-number">1.15.1.</span> <span class="nav-text">添加 TypeScript 到现有项目中</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F"><span class="nav-number">1.16.</span> <span class="nav-text">严格模式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BD%BF%E7%94%A8-PropTypes-%E8%BF%9B%E8%A1%8C%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%9F%A5"><span class="nav-number">1.17.</span> <span class="nav-text">使用 PropTypes 进行类型检查</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%9D%9E%E5%8F%97%E6%8E%A7%E7%BB%84%E4%BB%B6"><span class="nav-number">1.18.</span> <span class="nav-text">非受控组件</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%BB%98%E8%AE%A4%E5%80%BC"><span class="nav-number">1.18.1.</span> <span class="nav-text">默认值</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Web-Components"><span class="nav-number">1.19.</span> <span class="nav-text">Web Components</span></a></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">tallgy</p>
  <div class="site-description" itemprop="description">只是一个知识的搬运工</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">107</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">32</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">74</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/02/17/React/react-%E9%AB%98%E7%BA%A7%E6%8C%87%E5%BC%95/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          react-高级指引
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-02-17 11:00:05" itemprop="dateCreated datePublished" datetime="2022-02-17T11:00:05+08:00">2022-02-17</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/React/" itemprop="url" rel="index"><span itemprop="name">React</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h1 id="React-高级指引"><a href="#React-高级指引" class="headerlink" title="React 高级指引"></a>React 高级指引</h1><h2 id="无障碍"><a href="#无障碍" class="headerlink" title="无障碍"></a>无障碍</h2><h3 id="语义化的-HTML"><a href="#语义化的-HTML" class="headerlink" title="语义化的 HTML"></a>语义化的 HTML</h3><p>​        简单来说，就是如果对于比如 dl dt dd，其中这样的构造。</p>
<p>​        正常的使用 ，就是如同下面这样，因为return需要一个根组件，但是我们有两个标签，所以需要将其用div包含起来。</p>
<p>​        但是这样就会破坏语义性。所以就需要使用一个 Fragment 标签，其语义和template标签可以说是一样的。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">function ListItem(&#123; item &#125;) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;dt&gt;&#123;item.term&#125;&lt;/dt&gt;</span><br><span class="line">      &lt;dd&gt;&#123;item.description&#125;&lt;/dd&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function Glossary(props) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;dl&gt;</span><br><span class="line">      &#123;props.items.map(item =&gt; (</span><br><span class="line">        &lt;ListItem item=&#123;item&#125; key=&#123;item.id&#125; /&gt;</span><br><span class="line">      ))&#125;</span><br><span class="line">    &lt;/dl&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Fragment &#125; from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">function ListItem(&#123; item &#125;) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;Fragment&gt;</span><br><span class="line">      &lt;dt&gt;&#123;item.term&#125;&lt;/dt&gt;</span><br><span class="line">      &lt;dd&gt;&#123;item.description&#125;&lt;/dd&gt;</span><br><span class="line">    &lt;/Fragment&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function Glossary(props) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;dl&gt;</span><br><span class="line">      &#123;props.items.map(item =&gt; (</span><br><span class="line">        &lt;ListItem item=&#123;item&#125; key=&#123;item.id&#125; /&gt;</span><br><span class="line">      ))&#125;</span><br><span class="line">    &lt;/dl&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>​        和其他的元素一样，你可以把一系列的对象映射到一个 fragment 的数组中。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">function Glossary(props) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;dl&gt;</span><br><span class="line">      &#123;props.items.map(item =&gt; (</span><br><span class="line">        // Fragments should also have a `key` prop when mapping collections</span><br><span class="line">        &lt;Fragment key=&#123;item.id&#125;&gt;          &lt;dt&gt;&#123;item.term&#125;&lt;/dt&gt;</span><br><span class="line">          &lt;dd&gt;&#123;item.description&#125;&lt;/dd&gt;</span><br><span class="line">        &lt;/Fragment&gt;      ))&#125;</span><br><span class="line">    &lt;/dl&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        当你不需要在 fragment 标签中添加任何 prop 且你的工具支持的时候，你可以使用 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/fragments.html#short-syntax">短语法</a>：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">function ListItem(&#123; item &#125;) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;&gt;      &lt;dt&gt;&#123;item.term&#125;&lt;/dt&gt;</span><br><span class="line">      &lt;dd&gt;&#123;item.description&#125;&lt;/dd&gt;</span><br><span class="line">    &lt;/&gt;  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="控制焦点"><a href="#控制焦点" class="headerlink" title="控制焦点"></a>控制焦点</h3><p>​        我们可以使用 tab 键来进行焦点的切换。其中可以切换的是 a标签，input标签等可以获取focus的标签。</p>
<h3 id="使用程序管理焦点"><a href="#使用程序管理焦点" class="headerlink" title="使用程序管理焦点"></a>使用程序管理焦点</h3><p>​        简单来说，就是我们可以创建一个DOM元素ref，然后将其赋值给了ref。于是就可以通过 this.textInput.current 来获取当前DOM节点。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">class CustomTextInput extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    // 创造一个 textInput DOM 元素的 ref</span><br><span class="line">    this.textInput = React.createRef();</span><br><span class="line">  &#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">  // 使用 `ref` 回调函数以在实例的一个变量中存储文本输入 DOM 元素</span><br><span class="line">  //（比如，this.textInput）。</span><br><span class="line">    return (</span><br><span class="line">      &lt;input</span><br><span class="line">        type=&quot;text&quot;</span><br><span class="line">        ref=&#123;this.textInput&#125;</span><br><span class="line">      /&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        然后我们就可以在需要时于其他地方把焦点设置在这个组件上：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">focus() &#123;</span><br><span class="line">  // 使用原始的 DOM API 显式地聚焦在 text input 上</span><br><span class="line">  // 注意：我们通过访问 “current” 来获得 DOM 节点</span><br><span class="line">  this.textInput.current.focus();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        其大意和vue的ref一样，但是这里我们需要先通过React.createRef 来创建一个ref。然后再将其用ref赋值给标签则可以获得对应的DOM节点。</p>
<p>​        我们需要使用 current 来获取当前的节点。同时我们如果对一个值多次使用ref赋值，只有第一次的会有作用</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">我这里，div ref=&#123;this.textInput&#125;</span><br><span class="line">			CustomInput ref=&#123;this.textInput&#125;</span><br><span class="line">其最后的结果就是 div，current就只有这一个对象，并没有生成对应的多个对象出来。</span><br><span class="line"></span><br><span class="line">class App extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    private textInput : any</span><br><span class="line"></span><br><span class="line">    constructor(props : any) &#123;</span><br><span class="line">        super(props);</span><br><span class="line"></span><br><span class="line">        this.textInput = React.createRef();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line"></span><br><span class="line">    return (</span><br><span class="line">        &lt;div ref=&#123;this.textInput&#125;&gt;</span><br><span class="line">            &lt;CustomInput ref=&#123;this.textInput&#125; inputRef=&#123;this.textInput&#125;&gt;&lt;/CustomInput&gt;</span><br><span class="line">        &lt;/div&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        通过暴露ref，来让父组件的ref传向子节点。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">function CustomTextInput(props) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;input ref=&#123;props.inputRef&#125; /&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Parent extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    this.inputElement = React.createRef();</span><br><span class="line">  &#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">    return (</span><br><span class="line">      &lt;CustomTextInput inputRef=&#123;this.inputElement&#125; /&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 现在你就可以在需要时设置焦点了</span><br><span class="line">this.inputElement.current.focus();</span><br></pre></td></tr></table></figure>



<img src="/.io//image-20220217140334480.png" alt="image-20220217140334480" style="zoom:67%;">

<p>​        通过点击事件来让 ul 进行显示和隐藏，一般的想法就是使用click事件，设置一个Boolean值来进行显示和隐藏。</p>
<p>​        但是这样对于使用键盘的就不会过于的友好。因为这样点击事件启动之后，就需要其他的点击事件来进行操作会隐藏当前 ul 标签。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">button的点击事件</span><br><span class="line">onClickHandler() &#123;</span><br><span class="line">    this.setState(currentState =&gt; (&#123;</span><br><span class="line">      isOpen: !currentState.isOpen</span><br><span class="line">    &#125;));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">window对象监听点击事件。通过判断点击事件是否属于toggleContariner元素来确定是否需要隐藏。</span><br><span class="line">  onClickOutsideHandler(event) &#123;</span><br><span class="line">    if (this.state.isOpen &amp;&amp; !this.toggleContainer.current.contains(event.target)) &#123;</span><br><span class="line">      this.setState(&#123; isOpen: false &#125;);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>​        所以我们可以使用 onBlur 和 onFocus 来完成这个效果。</p>
<p>​        我的思考是，直接添加 onBlur 和 onFocus 来进行判断，虽然这样对于button的点击事件就会一直处于focus状态。</p>
<p>​        官网的思路就是，click事件还是添加在button上面，然后使用focus和blur来进行事件的处理。但是这里存在一个问题，那就是对于focus和blur的事件需要有能够进行事件的才会使用，但是官网案例的显示是ul标签，不存在焦点事件，所以出现了问题。</p>
<p>​        通过官网并不是获取焦点就直接为true，而是使用了 settimeout方法。在get focus的时候清除定时器。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line">class BlurExample extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line"></span><br><span class="line">    this.state = &#123; isOpen: false &#125;;</span><br><span class="line">    this.timeOutId = null;</span><br><span class="line"></span><br><span class="line">    this.onClickHandler = this.onClickHandler.bind(this);</span><br><span class="line">    this.onBlurHandler = this.onBlurHandler.bind(this);</span><br><span class="line">    this.onFocusHandler = this.onFocusHandler.bind(this);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  onClickHandler() &#123;</span><br><span class="line">    this.setState(currentState =&gt; (&#123;</span><br><span class="line">      isOpen: !currentState.isOpen</span><br><span class="line">    &#125;));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  // 我们在下一个时间点使用 setTimeout 关闭弹窗。</span><br><span class="line">  // 这是必要的，因为失去焦点事件会在新的焦点事件前被触发，</span><br><span class="line">  // 我们需要通过这个步骤确认这个元素的一个子节点</span><br><span class="line">  // 是否得到了焦点。</span><br><span class="line">  onBlurHandler() &#123;</span><br><span class="line">    this.timeOutId = setTimeout(() =&gt; &#123;</span><br><span class="line">      this.setState(&#123;</span><br><span class="line">        isOpen: false</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  // 如果一个子节点获得了焦点，不要关闭弹窗。</span><br><span class="line">  onFocusHandler() &#123;</span><br><span class="line">    clearTimeout(this.timeOutId);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    // React 通过把失去焦点和获得焦点事件传输给父节点</span><br><span class="line">    // 来帮助我们。</span><br><span class="line">    return (</span><br><span class="line">      &lt;div onBlur=&#123;this.onBlurHandler&#125;</span><br><span class="line">           onFocus=&#123;this.onFocusHandler&#125;&gt;</span><br><span class="line">        &lt;button onClick=&#123;this.onClickHandler&#125;</span><br><span class="line">                aria-haspopup=&quot;true&quot;</span><br><span class="line">                aria-expanded=&#123;this.state.isOpen&#125;&gt;</span><br><span class="line">          Select an option</span><br><span class="line">        &lt;/button&gt;</span><br><span class="line">        &#123;this.state.isOpen &amp;&amp; (</span><br><span class="line">          &lt;ul&gt;</span><br><span class="line">            &lt;li&gt;Option 1&lt;/li&gt;</span><br><span class="line">            &lt;li&gt;Option 2&lt;/li&gt;</span><br><span class="line">            &lt;li&gt;Option 3&lt;/li&gt;</span><br><span class="line">          &lt;/ul&gt;</span><br><span class="line">        )&#125;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="开发辅助"><a href="#开发辅助" class="headerlink" title="开发辅助"></a>开发辅助</h3><p>​        我们可以直接在 JSX 代码中检测一些无障碍复制功能。通常支持 JSX 的 IDE 会针对 ARIA roles，states 和 properties 提供智能检测。我们也可以使用以下工具：</p>
<h4 id="eslint-plugin-jsx-a11y"><a href="#eslint-plugin-jsx-a11y" class="headerlink" title="eslint-plugin-jsx-a11y"></a>eslint-plugin-jsx-a11y</h4><p>​        ESLint 中的 <a target="_blank" rel="noopener" href="https://github.com/evcohen/eslint-plugin-jsx-a11y">eslint-plugin-jsx-a11y</a> 插件为你的 JSX 中的无障碍问题提供了 AST 的语法检测反馈。许多 IDE 都允许你把这些发现直接集成到代码分析和源文件窗口中。</p>
<p>​         <a target="_blank" rel="noopener" href="https://github.com/facebookincubator/create-react-app">Create React App</a>中使用了这个插件中的一部分规则。如果你想启用更多的无障碍规则，你可以在项目的根目录中创建一个有如下内容的 <code>.eslintrc</code> 文件：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  &quot;extends&quot;: [&quot;react-app&quot;, &quot;plugin:jsx-a11y/recommended&quot;],</span><br><span class="line">  &quot;plugins&quot;: [&quot;jsx-a11y&quot;]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id><a href="#" class="headerlink" title></a></h3><h2 id="代码分割"><a href="#代码分割" class="headerlink" title="代码分割"></a>代码分割</h2><p>​        为了避免搞出大体积的代码包，在前期就思考该问题并对代码包进行分割是个不错的选择。 代码分割是由诸如 <a target="_blank" rel="noopener" href="https://webpack.docschina.org/guides/code-splitting/">Webpack</a>，<a target="_blank" rel="noopener" href="https://rollupjs.org/guide/en/#code-splitting">Rollup</a> 和 Browserify（<a target="_blank" rel="noopener" href="https://github.com/browserify/factor-bundle">factor-bundle</a>）这类打包器支持的一项技术，能够创建多个包并在运行时动态加载。</p>
<p>​        对你的应用进行代码分割能够帮助你“懒加载”当前用户所需要的内容，能够显著地提高你的应用性能。尽管并没有减少应用整体的代码体积，但你可以避免加载用户永远不需要的代码，并在初始加载的时候减少所需加载的代码量。</p>
<h3 id="import"><a href="#import" class="headerlink" title="import()"></a>import()</h3><p>​        在你的应用中引入代码分割的最佳方式是通过动态 <code>import()</code> 语法。</p>
<p>​        <strong>使用之前：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">import &#123; add &#125; from &#x27;./math&#x27;;</span><br><span class="line"></span><br><span class="line">console.log(add(16, 26));</span><br></pre></td></tr></table></figure>

<p>​        <strong>使用之后：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">import(&quot;./math&quot;).then(math =&gt; &#123;</span><br><span class="line">  console.log(math.add(16, 26));</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>​        当 Webpack 解析到该语法时，会自动进行代码分割。如果你使用 Create React App，该功能已开箱即用，你可以<a target="_blank" rel="noopener" href="https://create-react-app.dev/docs/code-splitting/">立刻使用</a>该特性。<a target="_blank" rel="noopener" href="https://nextjs.org/docs/advanced-features/dynamic-import">Next.js</a> 也已支持该特性而无需进行配置。</p>
<p>​        如果你自己配置 Webpack，你可能要阅读下 Webpack 关于<a target="_blank" rel="noopener" href="https://webpack.docschina.org/guides/code-splitting/">代码分割</a>的指南。你的 Webpack 配置应该<a target="_blank" rel="noopener" href="https://gist.github.com/gaearon/ca6e803f5c604d37468b0091d9959269">类似于此</a>。</p>
<p>​        当使用 <a target="_blank" rel="noopener" href="https://babeljs.io/">Babel</a> 时，你要确保 Babel 能够解析动态 import 语法而不是将其进行转换。对于这一要求你需要 <a target="_blank" rel="noopener" href="https://classic.yarnpkg.com/en/package/@babel/plugin-syntax-dynamic-import">@babel/plugin-syntax-dynamic-import</a> 插件。</p>
<h3 id="React-lazy"><a href="#React-lazy" class="headerlink" title="React.lazy"></a>React.lazy</h3><blockquote>
<p>注意:</p>
<p><code>React.lazy</code> 和 Suspense 技术还不支持服务端渲染。如果你想要在使用服务端渲染的应用中使用，我们推荐 <a target="_blank" rel="noopener" href="https://github.com/gregberge/loadable-components">Loadable Components</a> 这个库。它有一个很棒的<a target="_blank" rel="noopener" href="https://loadable-components.com/docs/server-side-rendering/">服务端渲染打包指南</a>。</p>
</blockquote>
<p><strong>使用之前：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">import OtherComponent from &#x27;./OtherComponent&#x27;;</span><br></pre></td></tr></table></figure>

<p><strong>使用之后：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const OtherComponent = React.lazy(() =&gt; import(&#x27;./OtherComponent&#x27;));</span><br></pre></td></tr></table></figure>

<p>​        此代码将会在组件首次渲染时，自动导入包含 <code>OtherComponent</code> 组件的包。</p>
<p>​        <code>React.lazy</code> 接受一个函数，这个函数需要动态调用 <code>import()</code>。它必须返回一个 <code>Promise</code>，该 Promise 需要 resolve 一个 <code>default</code> export 的 React 组件。</p>
<p>​        然后应在 <code>Suspense</code> 组件中渲染 lazy 组件，如此使得我们可以使用在等待加载 lazy 组件时做优雅降级（如 loading 指示器等）。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Suspense &#125; from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">const OtherComponent = React.lazy(() =&gt; import(&#x27;./OtherComponent&#x27;));</span><br><span class="line"></span><br><span class="line">function MyComponent() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Suspense fallback=&#123;&lt;div&gt;Loading...&lt;/div&gt;&#125;&gt;</span><br><span class="line">        &lt;OtherComponent /&gt;</span><br><span class="line">      &lt;/Suspense&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        主要有几个点：</p>
<ul>
<li>React.lazy() 进行promise加载</li>
<li>suspense 使用 里面的 fallback 来进行 加载过程中的优雅降级。</li>
</ul>
<p>​        <code>fallback</code> 属性接受任何在组件加载过程中你想展示的 React 元素。你可以将 <code>Suspense</code> 组件置于懒加载组件之上的任何位置。你甚至可以用一个 <code>Suspense</code> 组件包裹多个懒加载组件。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Suspense &#125; from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">const OtherComponent = React.lazy(() =&gt; import(&#x27;./OtherComponent&#x27;));</span><br><span class="line">const AnotherComponent = React.lazy(() =&gt; import(&#x27;./AnotherComponent&#x27;));</span><br><span class="line"></span><br><span class="line">function MyComponent() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Suspense fallback=&#123;&lt;div&gt;Loading...&lt;/div&gt;&#125;&gt;</span><br><span class="line">        &lt;section&gt;</span><br><span class="line">          &lt;OtherComponent /&gt;</span><br><span class="line">          &lt;AnotherComponent /&gt;</span><br><span class="line">        &lt;/section&gt;</span><br><span class="line">      &lt;/Suspense&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="异常捕获边界"><a href="#异常捕获边界" class="headerlink" title="异常捕获边界"></a>异常捕获边界</h3><p>​        如果模块加载失败（如网络问题），它会触发一个错误。你可以通过<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/error-boundaries.html">异常捕获边界（Error boundaries）</a>技术来处理这些情况，以显示良好的用户体验并管理恢复事宜。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Suspense &#125; from &#x27;react&#x27;;</span><br><span class="line">import MyErrorBoundary from &#x27;./MyErrorBoundary&#x27;;</span><br><span class="line"></span><br><span class="line">const OtherComponent = React.lazy(() =&gt; import(&#x27;./OtherComponent&#x27;));</span><br><span class="line">const AnotherComponent = React.lazy(() =&gt; import(&#x27;./AnotherComponent&#x27;));</span><br><span class="line"></span><br><span class="line">const MyComponent = () =&gt; (</span><br><span class="line">  &lt;div&gt;</span><br><span class="line">    &lt;MyErrorBoundary&gt;</span><br><span class="line">      &lt;Suspense fallback=&#123;&lt;div&gt;Loading...&lt;/div&gt;&#125;&gt;</span><br><span class="line">        &lt;section&gt;</span><br><span class="line">          &lt;OtherComponent /&gt;</span><br><span class="line">          &lt;AnotherComponent /&gt;</span><br><span class="line">        &lt;/section&gt;</span><br><span class="line">      &lt;/Suspense&gt;</span><br><span class="line">    &lt;/MyErrorBoundary&gt;</span><br><span class="line">  &lt;/div&gt;</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<p>​        具体的后续会说。</p>
<h3 id="基于路由的代码分割"><a href="#基于路由的代码分割" class="headerlink" title="基于路由的代码分割"></a>基于路由的代码分割</h3><p>​        这里是一个例子，展示如何在你的应用中使用 <code>React.lazy</code> 和 <a target="_blank" rel="noopener" href="https://reactrouter.com/">React Router</a> 这类的第三方库，来配置基于路由的代码分割。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Suspense, lazy &#125; from &#x27;react&#x27;;</span><br><span class="line">import &#123; BrowserRouter as Router, Routes, Route &#125; from &#x27;react-router-dom&#x27;;</span><br><span class="line"></span><br><span class="line">const Home = lazy(() =&gt; import(&#x27;./routes/Home&#x27;));</span><br><span class="line">const About = lazy(() =&gt; import(&#x27;./routes/About&#x27;));</span><br><span class="line"></span><br><span class="line">const App = () =&gt; (</span><br><span class="line">  &lt;Router&gt;</span><br><span class="line">    &lt;Suspense fallback=&#123;&lt;div&gt;Loading...&lt;/div&gt;&#125;&gt;</span><br><span class="line">      &lt;Routes&gt;</span><br><span class="line">        &lt;Route path=&quot;/&quot; element=&#123;&lt;Home /&gt;&#125; /&gt;</span><br><span class="line">        &lt;Route path=&quot;/about&quot; element=&#123;&lt;About /&gt;&#125; /&gt;</span><br><span class="line">      &lt;/Routes&gt;</span><br><span class="line">    &lt;/Suspense&gt;</span><br><span class="line">  &lt;/Router&gt;</span><br><span class="line">);</span><br></pre></td></tr></table></figure>



<h3 id="命名导出"><a href="#命名导出" class="headerlink" title="命名导出"></a>命名导出</h3><p>​        简单来说就是使用 React.lazy 方法。他只会使用默认导出。</p>
<p>​        <code>React.lazy</code> 目前只支持默认导出（default exports）。如果你想被引入的模块使用命名导出（named exports），你可以创建一个中间模块，来重新导出为默认模块。这能保证 tree shaking 不会出错，并且不必引入不需要的组件。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">// ManyComponents.js</span><br><span class="line">export const MyComponent = /* ... */;</span><br><span class="line">export const MyUnusedComponent = /* ... */;</span><br><span class="line"></span><br><span class="line">// MyComponent.js</span><br><span class="line">export &#123; MyComponent as default &#125; from &quot;./ManyComponents.js&quot;;</span><br><span class="line"></span><br><span class="line">// MyApp.js</span><br><span class="line">import React, &#123; lazy &#125; from &#x27;react&#x27;;</span><br><span class="line">const MyComponent = lazy(() =&gt; import(&quot;./MyComponent.js&quot;));</span><br></pre></td></tr></table></figure>



<h2 id="Context"><a href="#Context" class="headerlink" title="Context"></a>Context</h2><p>​        Context 提供了一个无需为每层组件手动添加 props，就能在组件树间进行数据传递的方法。</p>
<p>​        简单来说就是，因为props的是需要通过组件进行传递，那么此时，如果我们需要传递的属性极其繁琐。所以就会导致很麻烦，此时就可以使用context的形式。</p>
<p>​    <strong>例子：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">class ThemeButton extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        return &lt;button&gt;&#123;this.props.theme&#125;&lt;/button&gt;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Toolbar extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        return &lt;ThemeButton theme=&#123;this.props.theme&#125;&gt;&lt;/ThemeButton&gt;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class App extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &lt;input type=&quot;text&quot;/&gt;</span><br><span class="line">                &lt;Toolbar theme=&#123;&#x27;qqq&#x27;&#125;&gt;&lt;/Toolbar&gt;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        我们可以发现，App为了传递一个theme给ThemeButton ，从中间传递了几次，类似这种，传递次数又长，又冗余的。</p>
<p>​        使用 context 进行传递。</p>
<ul>
<li>首先，需要使用 const ThemeContext = React.createContext(‘light’); 进行创建一个context</li>
<li>然后，对于要传递的地方。使用 &lt;ThemeContext.Provider value=”dark”&gt; xxx &lt;/ThemeContext.Provider&gt; 标签进行包裹。这里的ThemeContext 就是创建context的命名。这里的 provider 就是代表了会将value进行传递给以下的组件树。</li>
<li>然后中间组件不需要传递 </li>
<li>对于要使用的，我们直接创建一个 static contextType = ThemeContext; 这里 contextType 是固定的。不可修改的。然后只需要再在标签里面通过 this.context 就可以访问了。</li>
<li>虽然感觉很奇怪。。</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">// Context 可以让我们无须明确地传遍每一个组件，就能将值深入传递进组件树。</span><br><span class="line">// 为当前的 theme 创建一个 context（“light”为默认值）。</span><br><span class="line">const ThemeContext = React.createContext(&#x27;light&#x27;);</span><br><span class="line">class App extends React.Component &#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。</span><br><span class="line">    // 无论多深，任何组件都能读取这个值。</span><br><span class="line">    // 在这个例子中，我们将 “dark” 作为当前的值传递下去。</span><br><span class="line">    return (</span><br><span class="line">      &lt;ThemeContext.Provider value=&quot;dark&quot;&gt;</span><br><span class="line">        &lt;Toolbar /&gt;</span><br><span class="line">      &lt;/ThemeContext.Provider&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 中间的组件再也不必指明往下传递 theme 了。</span><br><span class="line">function Toolbar() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;ThemedButton /&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class ThemedButton extends React.Component &#123;</span><br><span class="line">  // 指定 contextType 读取当前的 theme context。</span><br><span class="line">  // React 会往上找到最近的 theme Provider，然后使用它的值。</span><br><span class="line">  // 在这个例子中，当前的 theme 值为 “dark”。</span><br><span class="line">  static contextType = ThemeContext;</span><br><span class="line">  render() &#123;</span><br><span class="line">    return &lt;Button theme=&#123;this.context&#125; /&gt;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>​        <strong>如果你只是想避免层层传递一些属性，<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/composition-vs-inheritance.html">组件组合（component composition）</a>有时候是一个比 context 更好的解决方案。</strong></p>
<p>​        一种 <strong>无需 context</strong> 的解决方案是<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/composition-vs-inheritance.html#containment">将 <code>Avatar</code> 组件自身传递下去</a>，因为中间组件无需知道 <code>user</code> 或者 <code>avatarSize</code> 等 props：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">function Page(props) &#123;</span><br><span class="line">  const user = props.user;</span><br><span class="line">  const userLink = (</span><br><span class="line">    &lt;Link href=&#123;user.permalink&#125;&gt;</span><br><span class="line">      &lt;Avatar user=&#123;user&#125; size=&#123;props.avatarSize&#125; /&gt;</span><br><span class="line">    &lt;/Link&gt;</span><br><span class="line">  );</span><br><span class="line">  return &lt;PageLayout userLink=&#123;userLink&#125; /&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 现在，我们有这样的组件：</span><br><span class="line">&lt;Page user=&#123;user&#125; avatarSize=&#123;avatarSize&#125; /&gt;</span><br><span class="line">// ... 渲染出 ...</span><br><span class="line">&lt;PageLayout userLink=&#123;...&#125; /&gt;</span><br><span class="line">// ... 渲染出 ...</span><br><span class="line">&lt;NavigationBar userLink=&#123;...&#125; /&gt;</span><br><span class="line">// ... 渲染出 ...</span><br><span class="line">&#123;props.userLink&#125;</span><br></pre></td></tr></table></figure>

<p>​        简单来说就是，我们传递的时候，将组件传递过去，然后中间层，只需要传递组件props，而不会传递其他的，就轻松很多，如同一种插槽一样的感觉。</p>
<h3 id="React-createContext-API"><a href="#React-createContext-API" class="headerlink" title="React.createContext API"></a><code>React.createContext</code> API</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const MyContext = React.createContext(defaultValue);</span><br></pre></td></tr></table></figure>

<p>​        创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件，这个组件会从组件树中离自身最近的那个匹配的 <code>Provider</code> 中读取到当前的 context 值。</p>
<p>​        <strong>只有</strong>当组件所处的树中没有匹配到 Provider 时，其 <code>defaultValue</code> 参数才会生效。此默认值有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意：将 <code>undefined</code> 传递给 Provider 的 value 时，消费组件的 <code>defaultValue</code> 不会生效。</p>
<p>​        具体的查看官方文档进行了解。</p>
<h2 id="错误边界"><a href="#错误边界" class="headerlink" title="错误边界"></a>错误边界</h2><p>​        错误边界是一种 React 组件，这种组件<strong>可以捕获发生在其子组件树任何位置的 JavaScript 错误，并打印这些错误，同时展示降级 UI</strong>，而并不会渲染那些发生崩溃的子组件树。错误边界可以捕获发生在整个子组件树的渲染期间、生命周期方法以及构造函数中的错误。</p>
<blockquote>
<p>注意</p>
<p>错误边界<strong>无法</strong>捕获以下场景中产生的错误：</p>
<ul>
<li>事件处理（<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/error-boundaries.html#how-about-event-handlers">了解更多</a>）</li>
<li>异步代码（例如 <code>setTimeout</code> 或 <code>requestAnimationFrame</code> 回调函数）</li>
<li>服务端渲染</li>
<li>它自身抛出来的错误（并非它的子组件）</li>
</ul>
</blockquote>
<p>​        如果一个 class 组件中定义了 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-component.html#static-getderivedstatefromerror"><code>static getDerivedStateFromError()</code></a> 或 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-component.html#componentdidcatch"><code>componentDidCatch()</code></a> 这两个生命周期方法中的任意一个（或两个）时，那么它就变成一个错误边界。当抛出错误后，请使用 <code>static getDerivedStateFromError()</code> 渲染备用 UI ，使用 <code>componentDidCatch()</code> 打印错误信息。</p>
<ul>
<li>static getDerivedStateFromError()<ul>
<li>渲染备用UI</li>
</ul>
</li>
<li>componentDidCatch()<ul>
<li>打印错误信息</li>
</ul>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">class ErrorBoundary extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    this.state = &#123; hasError: false &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  static getDerivedStateFromError(error) &#123;</span><br><span class="line">    // 更新 state 使下一次渲染能够显示降级后的 UI</span><br><span class="line">    return &#123; hasError: true &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  componentDidCatch(error, errorInfo) &#123;</span><br><span class="line">    // 你同样可以将错误日志上报给服务器</span><br><span class="line">    logErrorToMyService(error, errorInfo);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    if (this.state.hasError) &#123;</span><br><span class="line">      // 你可以自定义降级后的 UI 并渲染</span><br><span class="line">      return &lt;h1&gt;Something went wrong.&lt;/h1&gt;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    return this.props.children; </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        然后你可以将它作为一个常规组件去使用：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;ErrorBoundary&gt;</span><br><span class="line">  &lt;MyWidget /&gt;</span><br><span class="line">&lt;/ErrorBoundary&gt;</span><br></pre></td></tr></table></figure>

<p>​        错误边界的工作方式类似于 JavaScript 的 <code>catch &#123;&#125;</code>，不同的地方在于错误边界只针对 React 组件。只有 class 组件才可以成为错误边界组件。大多数情况下, 你只需要声明一次错误边界组件, 并在整个应用中使用它。</p>
<p>​        注意<strong>错误边界仅可以捕获其子组件的错误</strong>，它无法捕获其自身的错误。如果一个错误边界无法渲染错误信息，则错误会冒泡至最近的上层错误边界，这也类似于 JavaScript 中 <code>catch &#123;&#125;</code> 的工作机制。</p>
<h3 id="未捕获错误的新行为"><a href="#未捕获错误的新行为" class="headerlink" title="未捕获错误的新行为"></a>未捕获错误的新行为</h3><p>​        这一改变具有重要意义，<strong>自 React 16 起，任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载。</strong></p>
<h3 id="组件栈追踪"><a href="#组件栈追踪" class="headerlink" title="组件栈追踪"></a>组件栈追踪</h3><blockquote>
<p>注意</p>
<p>组件名称在栈追踪中的显示依赖于 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name"><code>Function.name</code></a> 属性。如果你想要支持尚未提供该功能的旧版浏览器和设备（例如 IE 11），考虑在你的打包（bundled）应用程序中包含一个 <code>Function.name</code> 的 polyfill，如 <a target="_blank" rel="noopener" href="https://github.com/JamesMGreene/Function.name"><code>function.name-polyfill</code></a> 。或者，你可以在所有组件上显式设置 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-component.html#displayname"><code>displayName</code></a> 属性。</p>
</blockquote>
<h2 id="refs转发"><a href="#refs转发" class="headerlink" title="refs转发"></a>refs转发</h2><p>​        Ref 转发是一项将 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/refs-and-the-dom.html">ref</a> 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说，这通常不是必需的。但其对某些组件，尤其是可重用的组件库是很有用的。最常见的案例如下所述。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">class App extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        const FancyButton = React.forwardRef((props, ref) =&gt; (</span><br><span class="line">            &lt;button ref=&#123;ref as any&#125; className=&quot;FancyButton&quot;&gt;</span><br><span class="line">                &#123;props.children&#125;</span><br><span class="line">            &lt;/button&gt;</span><br><span class="line">        )) as any;</span><br><span class="line"></span><br><span class="line">        // 你可以直接获取 DOM button 的 ref：</span><br><span class="line">        const ref = React.createRef();</span><br><span class="line">        return &lt;FancyButton ref=&#123;ref&#125;&gt;Click me!&lt;/FancyButton&gt;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="Fragments"><a href="#Fragments" class="headerlink" title="Fragments"></a>Fragments</h2><p>​        React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组，而无需向 DOM 添加额外节点。</p>
<p>​        还有一种新的<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/fragments.html#short-syntax">短语法</a>可用于声明它们。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">render() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;React.Fragment&gt;</span><br><span class="line">      &lt;ChildA /&gt;</span><br><span class="line">      &lt;ChildB /&gt;</span><br><span class="line">      &lt;ChildC /&gt;</span><br><span class="line">    &lt;/React.Fragment&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">render() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;&gt;</span><br><span class="line">      &lt;ChildA /&gt;</span><br><span class="line">      &lt;ChildB /&gt;</span><br><span class="line">      &lt;ChildC /&gt;</span><br><span class="line">    &lt;/&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        作用，就是可以在使用的时候不会影响到DOM树，因为他不会渲染成DOM树。和template相似。</p>
<p>​        同时，可以携带key值，用于循环。</p>
<h2 id="高阶组件"><a href="#高阶组件" class="headerlink" title="高阶组件"></a>高阶组件</h2><p>​        高阶组件（HOC）是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分，它是一种基于 React 的组合特性而形成的设计模式。</p>
<p>​        具体而言，<strong>高阶组件是参数为组件，返回值为新组件的函数。</strong></p>
<p>​        同时高阶组件是一个纯函数。不会修改原来的参数。</p>
<p>​        <code>CommentList</code> 和 <code>BlogPost</code> 不同 - 它们在 <code>DataSource</code> 上调用不同的方法，且渲染不同的结果。但它们的大部分实现都是一样的：</p>
<ul>
<li>在挂载时，向 <code>DataSource</code> 添加一个更改侦听器。</li>
<li>在侦听器内部，当数据源发生变化时，调用 <code>setState</code>。</li>
<li>在卸载时，删除侦听器。</li>
</ul>
<p>​        你可以想象，在一个大型应用程序中，这种订阅 <code>DataSource</code> 和调用 <code>setState</code> 的模式将一次又一次地发生。我们需要一个抽象，允许我们在一个地方定义这个逻辑，并在许多组件之间共享它。这正是高阶组件擅长的地方。</p>
<p>​        对于订阅了 <code>DataSource</code> 的组件，比如 <code>CommentList</code> 和 <code>BlogPost</code>，我们可以编写一个创建组件函数。该函数将接受一个子组件作为它的其中一个参数，该子组件将订阅数据作为 prop。让我们调用函数 <code>withSubscription</code>：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">const CommentListWithSubscription = withSubscription(</span><br><span class="line">  CommentList,</span><br><span class="line">  (DataSource) =&gt; DataSource.getComments()</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">const BlogPostWithSubscription = withSubscription(</span><br><span class="line">  BlogPost,</span><br><span class="line">  (DataSource, props) =&gt; DataSource.getBlogPost(props.id)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">// 此函数接收一个组件...</span><br><span class="line">function withSubscription(WrappedComponent, selectData) &#123;</span><br><span class="line">  // ...并返回另一个组件...</span><br><span class="line">  return class extends React.Component &#123;</span><br><span class="line">    constructor(props) &#123;</span><br><span class="line">      super(props);</span><br><span class="line">      this.handleChange = this.handleChange.bind(this);</span><br><span class="line">      this.state = &#123;</span><br><span class="line">        data: selectData(DataSource, props)</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    componentDidMount() &#123;</span><br><span class="line">      // ...负责订阅相关的操作...</span><br><span class="line">      DataSource.addChangeListener(this.handleChange);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    componentWillUnmount() &#123;</span><br><span class="line">      DataSource.removeChangeListener(this.handleChange);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    handleChange() &#123;</span><br><span class="line">      this.setState(&#123;</span><br><span class="line">        data: selectData(DataSource, this.props)</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">      // ... 并使用新数据渲染被包装的组件!</span><br><span class="line">      // 请注意，我们可能还会传递其他属性</span><br><span class="line">      return &lt;WrappedComponent data=&#123;this.state.data&#125; &#123;...this.props&#125; /&gt;;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="深入JSX"><a href="#深入JSX" class="headerlink" title="深入JSX"></a>深入JSX</h2><p>​        实际上，JSX 仅仅只是 <code>React.createElement(component, props, ...children)</code> 函数的语法糖。如下 JSX 代码：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;MyButton color=&quot;blue&quot; shadowSize=&#123;2&#125;&gt;</span><br><span class="line">  Click Me</span><br><span class="line">&lt;/MyButton&gt;</span><br></pre></td></tr></table></figure>

<p>会编译为：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">React.createElement(</span><br><span class="line">  MyButton,</span><br><span class="line">  &#123;color: &#x27;blue&#x27;, shadowSize: 2&#125;,</span><br><span class="line">  &#x27;Click Me&#x27;</span><br><span class="line">)</span><br></pre></td></tr></table></figure>

<h3 id="指定-React-元素类型"><a href="#指定-React-元素类型" class="headerlink" title="指定 React 元素类型"></a>指定 React 元素类型</h3><p>​        JSX 标签的第一部分指定了 React 元素的类型。</p>
<p>​        大写字母开头的 JSX 标签意味着它们是 React 组件。这些标签会被编译为对命名变量的直接引用，所以，当你使用 JSX <code>&lt;Foo /&gt;</code> 表达式时，<code>Foo</code> 必须包含在作用域内。</p>
<h4 id="React-必须在作用域内"><a href="#React-必须在作用域内" class="headerlink" title="React 必须在作用域内"></a>React 必须在作用域内</h4><p>​        由于 JSX 会编译为 <code>React.createElement</code> 调用形式，所以 <code>React</code> 库也必须包含在 JSX 代码作用域内。</p>
<p>​        例如，在如下代码中，虽然 <code>React</code> 和 <code>CustomButton</code> 并没有被直接使用，但还是需要导入：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;import CustomButton from &#x27;./CustomButton&#x27;;</span><br><span class="line">function WarningButton() &#123;</span><br><span class="line">  // return React.createElement(CustomButton, &#123;color: &#x27;red&#x27;&#125;, null);  return &lt;CustomButton color=&quot;red&quot; /&gt;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="在-JSX-类型中使用点语法"><a href="#在-JSX-类型中使用点语法" class="headerlink" title="在 JSX 类型中使用点语法"></a>在 JSX 类型中使用点语法</h4><p>​        在 JSX 中，你也可以使用点语法来引用一个 React 组件。当你在一个模块中导出许多 React 组件时，这会非常方便。例如，如果 <code>MyComponents.DatePicker</code> 是一个组件，你可以在 JSX 中直接使用：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">const MyComponents = &#123;</span><br><span class="line">  DatePicker: function DatePicker(props) &#123;</span><br><span class="line">    return &lt;div&gt;Imagine a &#123;props.color&#125; datepicker here.&lt;/div&gt;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function BlueDatePicker() &#123;</span><br><span class="line">  return &lt;MyComponents.DatePicker color=&quot;blue&quot; /&gt;;&#125;</span><br></pre></td></tr></table></figure>

<h4 id="用户定义的组件必须以大写字母开头"><a href="#用户定义的组件必须以大写字母开头" class="headerlink" title="用户定义的组件必须以大写字母开头"></a>用户定义的组件必须以大写字母开头</h4><h4 id="在运行时选择类型"><a href="#在运行时选择类型" class="headerlink" title="在运行时选择类型"></a>在运行时选择类型</h4><p>​        你不能将通用表达式作为 React 元素类型。如果你想通过通用表达式来（动态）决定元素类型，你需要首先将它赋值给大写字母开头的变量。这通常用于根据 prop 来渲染不同组件的情况下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;</span><br><span class="line">import &#123; PhotoStory, VideoStory &#125; from &#x27;./stories&#x27;;</span><br><span class="line"></span><br><span class="line">const components = &#123;</span><br><span class="line">  photo: PhotoStory,</span><br><span class="line">  video: VideoStory</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">function Story(props) &#123;</span><br><span class="line">  // 错误！JSX 类型不能是一个表达式。  return &lt;components[props.storyType] story=&#123;props.story&#125; /&gt;;&#125;</span><br></pre></td></tr></table></figure>

<p>​        要解决这个问题, 需要首先将类型赋值给一个大写字母开头的变量：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;</span><br><span class="line">import &#123; PhotoStory, VideoStory &#125; from &#x27;./stories&#x27;;</span><br><span class="line"></span><br><span class="line">const components = &#123;</span><br><span class="line">  photo: PhotoStory,</span><br><span class="line">  video: VideoStory</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">function Story(props) &#123;</span><br><span class="line">  // 正确！JSX 类型可以是大写字母开头的变量。  const SpecificStory = components[props.storyType];  return &lt;SpecificStory story=&#123;props.story&#125; /&gt;;&#125;</span><br></pre></td></tr></table></figure>

<h3 id="JSX-中的-Props"><a href="#JSX-中的-Props" class="headerlink" title="JSX 中的 Props"></a>JSX 中的 Props</h3><h4 id="字符串字面量"><a href="#字符串字面量" class="headerlink" title="字符串字面量"></a>字符串字面量</h4><p>​        你可以将字符串字面量赋值给 prop. 如下两个 JSX 表达式是等价的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;MyComponent message=&quot;hello world&quot; /&gt;</span><br><span class="line"></span><br><span class="line">&lt;MyComponent message=&#123;&#x27;hello world&#x27;&#125; /&gt;</span><br></pre></td></tr></table></figure>

<p>​        当你将字符串字面量赋值给 prop 时，它的值是未转义的。所以，以下两个 JSX 表达式是等价的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;MyComponent message=&quot;&amp;lt;3&quot; /&gt;</span><br><span class="line"></span><br><span class="line">&lt;MyComponent message=&#123;&#x27;&lt;3&#x27;&#125; /&gt;</span><br></pre></td></tr></table></figure>

<h4 id="Props-默认值为-“True”"><a href="#Props-默认值为-“True”" class="headerlink" title="Props 默认值为 “True”"></a>Props 默认值为 “True”</h4><p>​        如果你没给 prop 赋值，它的默认值是 <code>true</code>。以下两个 JSX 表达式是等价的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;MyTextBox autocomplete /&gt;</span><br><span class="line"></span><br><span class="line">&lt;MyTextBox autocomplete=&#123;true&#125; /&gt;</span><br></pre></td></tr></table></figure>

<p>​        通常，我们不建议不传递 value 给 prop，因为这可能与 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_2015">ES6 对象简写</a>混淆，<code>&#123;foo&#125;</code> 是 <code>&#123;foo: foo&#125;</code> 的简写，而不是 <code>&#123;foo: true&#125;</code>。这样实现只是为了保持和 HTML 中标签属性的行为一致。</p>
<h4 id="属性展开"><a href="#属性展开" class="headerlink" title="属性展开"></a>属性展开</h4><p>​        如果你已经有了一个 props 对象，你可以使用展开运算符 <code>...</code> 来在 JSX 中传递整个 props 对象。以下两个组件是等价的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">function App1() &#123;</span><br><span class="line">  return &lt;Greeting firstName=&quot;Ben&quot; lastName=&quot;Hector&quot; /&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function App2() &#123;</span><br><span class="line">  const props = &#123;firstName: &#x27;Ben&#x27;, lastName: &#x27;Hector&#x27;&#125;;</span><br><span class="line">  return &lt;Greeting &#123;...props&#125; /&gt;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="JSX-子元素"><a href="#JSX-子元素" class="headerlink" title="JSX 子元素"></a>JSX 子元素</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">render() &#123;</span><br><span class="line">  // 不需要用额外的元素包裹列表元素！</span><br><span class="line">  return [</span><br><span class="line">    // 不要忘记设置 key :)</span><br><span class="line">    &lt;li key=&quot;A&quot;&gt;First item&lt;/li&gt;,</span><br><span class="line">    &lt;li key=&quot;B&quot;&gt;Second item&lt;/li&gt;,</span><br><span class="line">    &lt;li key=&quot;C&quot;&gt;Third item&lt;/li&gt;,</span><br><span class="line">  ];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="函数作为子元素"><a href="#函数作为子元素" class="headerlink" title="函数作为子元素"></a>函数作为子元素</h4><p>​        通常，JSX 中的 JavaScript 表达式将会被计算为字符串、React 元素或者是列表。不过，<code>props.children</code> 和其他 prop 一样，它可以传递任意类型的数据，而不仅仅是 React 已知的可渲染类型。例如，如果你有一个自定义组件，你可以把回调函数作为 <code>props.children</code> 进行传递：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">// 调用子元素回调 numTimes 次，来重复生成组件</span><br><span class="line">function Repeat(props) &#123;</span><br><span class="line">  let items = [];</span><br><span class="line">  for (let i = 0; i &lt; props.numTimes; i++) &#123;    items.push(props.children(i));</span><br><span class="line">  &#125;</span><br><span class="line">  return &lt;div&gt;&#123;items&#125;&lt;/div&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function ListOfTenThings() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;Repeat numTimes=&#123;10&#125;&gt;</span><br><span class="line">      &#123;(index) =&gt; &lt;div key=&#123;index&#125;&gt;This is item &#123;index&#125; in the list&lt;/div&gt;&#125;    &lt;/Repeat&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="布尔类型、Null-以及-Undefined-将会忽略"><a href="#布尔类型、Null-以及-Undefined-将会忽略" class="headerlink" title="布尔类型、Null 以及 Undefined 将会忽略"></a>布尔类型、Null 以及 Undefined 将会忽略</h4><p>​        <code>false</code>, <code>null</code>, <code>undefined</code>, and <code>true</code> 是合法的子元素。但它们并不会被渲染。以下的 JSX 表达式渲染结果相同：</p>
<p>​        值得注意的是有一些 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy">“falsy” 值</a>，如数字 <code>0</code>，仍然会被 React 渲染。例如，以下代码并不会像你预期那样工作，因为当 <code>props.messages</code> 是空数组时，<code>0</code> 仍然会被渲染：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;div&gt;</span><br><span class="line">  &#123;props.messages.length &amp;&amp;    &lt;MessageList messages=&#123;props.messages&#125; /&gt;</span><br><span class="line">  &#125;</span><br><span class="line">&lt;/div&gt;</span><br></pre></td></tr></table></figure>



<h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2><p>​        UI 更新需要昂贵的 DOM 操作，因此 React 内部使用了几种巧妙的技术来最小化 DOM 操作次数。对于大部分应用而言，使用 React 时无需做大量优化工作就能拥有高性能的用户界面。尽管如此，你仍然有办法来加速你的 React 应用。</p>
<h3 id="虚拟化长列表"><a href="#虚拟化长列表" class="headerlink" title="虚拟化长列表"></a>虚拟化长列表</h3><p>​        如果你的应用渲染了长列表（上百甚至上千的数据），我们推荐使用“虚拟滚动”技术。这项技术会在有限的时间内仅渲染有限的内容，并奇迹般地降低重新渲染组件消耗的时间，以及创建 DOM 节点的数量。</p>
<p>​        <a target="_blank" rel="noopener" href="https://react-window.now.sh/">react-window</a> 和 <a target="_blank" rel="noopener" href="https://bvaughn.github.io/react-virtualized/">react-virtualized</a> 是热门的虚拟滚动库。 它们提供了多种可复用的组件，用于展示列表、网格和表格数据。 如果你想要一些针对你的应用做定制优化，你也可以创建你自己的虚拟滚动组件，就像 <a target="_blank" rel="noopener" href="https://medium.com/@paularmstrong/twitter-lite-and-high-performance-react-progressive-web-apps-at-scale-d28a00e780a3">Twitter 所做的</a>。</p>
<h3 id="避免调停"><a href="#避免调停" class="headerlink" title="避免调停"></a>避免调停</h3><p>​        React 构建并维护了一套内部的 UI 渲染描述。它包含了来自你的组件返回的 React 元素。该描述使得 React 避免创建 DOM 节点以及没有必要的节点访问，因为 DOM 操作相对于 JavaScript 对象操作更慢。虽然有时候它被称为“虚拟 DOM”，但是它在 React Native 中拥有相同的工作原理。</p>
<p>​        当一个组件的 props 或 state 变更，React 会将最新返回的元素与之前渲染的元素进行对比，以此决定是否有必要更新真实的 DOM。当它们不相同时，React 会更新该 DOM。</p>
<p>​        即使 React 只更新改变了的 DOM 节点，重新渲染仍然花费了一些时间。在大部分情况下它并不是问题，不过如果它已经慢到让人注意了，你可以通过覆盖生命周期方法 <code>shouldComponentUpdate</code> 来进行提速。该方法会在重新渲染前被触发。其默认实现总是返回 <code>true</code>，让 React 执行更新：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">shouldComponentUpdate(nextProps, nextState) &#123;</span><br><span class="line">  return true;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        如果你知道在什么情况下你的组件不需要更新，你可以在 <code>shouldComponentUpdate</code> 中返回 <code>false</code> 来跳过整个渲染过程。其包括该组件的 <code>render</code> 调用以及之后的操作。</p>
<p>​        在大部分情况下，你可以继承 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-api.html#reactpurecomponent"><code>React.PureComponent</code></a> 以代替手写 <code>shouldComponentUpdate()</code>。它用当前与之前 props 和 state 的浅比较覆写了 <code>shouldComponentUpdate()</code> 的实现。</p>
<h3 id="shouldComponentUpdate-的作用"><a href="#shouldComponentUpdate-的作用" class="headerlink" title="shouldComponentUpdate 的作用"></a>shouldComponentUpdate 的作用</h3><p>​        这是一个组件的子树。每个节点中，<code>SCU</code> 代表 <code>shouldComponentUpdate</code> 返回的值，而 <code>vDOMEq</code> 代表返回的 React 元素是否相同。最后，圆圈的颜色代表了该组件是否需要被调停。</p>
<p><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/static/5ee1bdf4779af06072a17b7a0654f6db/cd039/should-component-update.png"><img src="/.io//should-component-update.png" alt="should component update"></a></p>
<p>​        节点 C2 的 <code>shouldComponentUpdate</code> 返回了 <code>false</code>，React 因而不会去渲染 C2，也因此 C4 和 C5 的 <code>shouldComponentUpdate</code> 不会被调用到。</p>
<p>​        对于 C1 和 C3，<code>shouldComponentUpdate</code> 返回了 <code>true</code>，所以 React 需要继续向下查询子节点。这里 C6 的 <code>shouldComponentUpdate</code> 返回了 <code>true</code>，同时由于渲染的元素与之前的不同使得 React 更新了该 DOM。</p>
<p>​        最后一个有趣的例子是 C8。React 需要渲染这个组件，但是由于其返回的 React 元素和之前渲染的相同，所以不需要更新 DOM。</p>
<p>​        显而易见，你看到 React 只改变了 C6 的 DOM。对于 C8，通过对比了渲染的 React 元素跳过了渲染。而对于 C2 的子节点和 C7，由于 <code>shouldComponentUpdate</code> 使得 <code>render</code> 并没有被调用。因此它们也不需要对比元素了。</p>
<h4 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">class CounterButton extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    this.state = &#123;count: 1&#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  shouldComponentUpdate(nextProps, nextState) &#123;</span><br><span class="line">    if (this.props.color !== nextProps.color) &#123;</span><br><span class="line">      return true;</span><br><span class="line">    &#125;</span><br><span class="line">    if (this.state.count !== nextState.count) &#123;</span><br><span class="line">      return true;</span><br><span class="line">    &#125;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    return (</span><br><span class="line">      &lt;button</span><br><span class="line">        color=&#123;this.props.color&#125;</span><br><span class="line">        onClick=&#123;() =&gt; this.setState(state =&gt; (&#123;count: state.count + 1&#125;))&#125;&gt;</span><br><span class="line">        Count: &#123;this.state.count&#125;</span><br><span class="line">      &lt;/button&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="Portals"><a href="#Portals" class="headerlink" title="Portals"></a>Portals</h2><p>​        Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ReactDOM.createPortal(child, container)</span><br></pre></td></tr></table></figure>

<p>​        第一个参数（<code>child</code>）是任何<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-component.html#render">可渲染的 React 子元素</a>，例如一个元素，字符串或 fragment。第二个参数（<code>container</code>）是一个 DOM 元素。</p>
<h2 id="Profiler-API"><a href="#Profiler-API" class="headerlink" title="Profiler API"></a>Profiler API</h2><p>​        <code>Profiler</code> 测量一个 React 应用多久渲染一次以及渲染一次的“代价”。 它的目的是识别出应用中渲染较慢的部分，或是可以使用<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/hooks-faq.html#how-to-memoize-calculations">类似 memoization 优化</a>的部分，并从相关优化中获益。</p>
<blockquote>
<p>注意：</p>
<p>Profiling 增加了额外的开支，所以<strong>它在<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/optimizing-performance.html#use-the-production-build">生产构建</a>中会被禁用</strong>。</p>
<p>为了将 profiling 功能加入生产环境中，React 提供了使 profiling 可用的特殊的生产构建环境。 从 <a target="_blank" rel="noopener" href="https://fb.me/react-profiling">fb.me/react-profiling</a>了解更多关于如何使用这个构建环境的信息。</p>
</blockquote>
<h2 id="协调"><a href="#协调" class="headerlink" title="协调"></a>协调</h2><p>​        React 提供的声明式 API 让开发者可以在对 React 的底层实现并不了解的情况下编写应用。在开发者编写应用时，可以保持相对简单的心智，但开发者无法了解其内部的实现原理。本文描述了在实现 React 的 “diffing” 算法过程中所作出的设计决策，以保证组件更新可预测，且在繁杂业务场景下依然保持应用的高性能。</p>
<h3 id="设计动机"><a href="#设计动机" class="headerlink" title="设计动机"></a>设计动机</h3><p>​        在某一时间节点调用 React 的 <code>render()</code> 方法，会创建一棵由 React 元素组成的树。在下一次 state 或 props 更新时，相同的 <code>render()</code> 方法会返回一棵不同的树。React 需要基于这两棵树之间的差别来判断如何高效的更新 UI，以保证当前 UI 与最新的树保持同步。</p>
<p>​        通常一个最优算法里面。时间复杂度依然很高。</p>
<p>​        所以React在以下的两个基础之上提出了另外一套算法。同时也是diffing算法的核心。（我认为）</p>
<ul>
<li>两个不同类型的元素会产生出不同的树</li>
<li>开发者可以通过设置 key 属性，来告知那些子元素在不同的渲染下可以保存不变。</li>
</ul>
<h3 id="diffing-算法"><a href="#diffing-算法" class="headerlink" title="diffing 算法"></a>diffing 算法</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://zh-hans.reactjs.org/docs/reconciliation.html#the-diffing-algorithm</span><br></pre></td></tr></table></figure>

<h4 id="对比不同类型的元素"><a href="#对比不同类型的元素" class="headerlink" title="对比不同类型的元素"></a>对比不同类型的元素</h4><p>​        当根节点为不同类型的元素时，React 会拆卸原有的树并且建立起新的树。举个例子，当一个元素从 <code>&lt;a&gt;</code> 变成 <code>&lt;img&gt;</code>，从 <code>&lt;Article&gt;</code> 变成 <code>&lt;Comment&gt;</code>，或从 <code>&lt;Button&gt;</code> 变成 <code>&lt;div&gt;</code> 都会触发一个完整的重建流程。</p>
<h4 id="对比同一类型的元素"><a href="#对比同一类型的元素" class="headerlink" title="对比同一类型的元素"></a>对比同一类型的元素</h4><p>​        当对比两个相同类型的 React 元素时，React 会保留 DOM 节点，仅比对及更新有改变的属性。比如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;div className=&quot;before&quot; title=&quot;stuff&quot; /&gt;</span><br><span class="line"></span><br><span class="line">&lt;div className=&quot;after&quot; title=&quot;stuff&quot; /&gt;</span><br></pre></td></tr></table></figure>

<p>​        通过对比这两个元素，React 知道只需要修改 DOM 元素上的 <code>className</code> 属性。</p>
<p>​        当更新 <code>style</code> 属性时，React 仅更新有所更变的属性。比如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;div style=&#123;&#123;color: &#x27;red&#x27;, fontWeight: &#x27;bold&#x27;&#125;&#125; /&gt;</span><br><span class="line"></span><br><span class="line">&lt;div style=&#123;&#123;color: &#x27;green&#x27;, fontWeight: &#x27;bold&#x27;&#125;&#125; /&gt;</span><br></pre></td></tr></table></figure>

<p>​        通过对比这两个元素，React 知道只需要修改 DOM 元素上的 <code>color</code> 样式，无需修改 <code>fontWeight</code>。</p>
<p>​        在处理完当前节点之后，React 继续对子节点进行递归。</p>
<h4 id="对比同类型的组件元素"><a href="#对比同类型的组件元素" class="headerlink" title="对比同类型的组件元素"></a>对比同类型的组件元素</h4><p>​        当一个组件更新时，组件实例会保持不变，因此可以在不同的渲染时保持 state 一致。</p>
<p>​        下一步，调用 <code>render()</code> 方法，diff 算法将在之前的结果以及新的结果中进行递归。</p>
<h4 id="对子节点进行递归"><a href="#对子节点进行递归" class="headerlink" title="对子节点进行递归"></a>对子节点进行递归</h4><p>​        默认情况下，当递归 DOM 节点的子元素时，React 会同时遍历两个子元素的列表；当产生差异时，生成一个 mutation。</p>
<ul>
<li><p>在子元素列表末尾新增元素时，更新开销比较小。</p>
</li>
<li><p>如果只是简单的将新增元素插入到表头，那么更新开销会比较大。</p>
<ul>
<li>因为React 不会意识到应该保留其他元素。所以会导致开销比较大。</li>
<li>所以需要使用keys</li>
</ul>
</li>
</ul>
<h4 id="Keys"><a href="#Keys" class="headerlink" title="Keys"></a>Keys</h4><p>​        为了解决上述问题，React 引入了 <code>key</code> 属性。当子元素拥有 key 时，React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。</p>
<p>​        当基于下标的组件进行重新排序时，组件 state 可能会遇到一些问题。由于组件实例是基于它们的 key 来决定是否更新以及复用，如果 key 是一个下标，那么修改顺序时会修改当前的 key，导致非受控组件的 state（比如输入框）可能相互篡改，会出现无法预期的变动。</p>
<p>​        在 Codepen 有两个例子，分别为 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/redirect-to-codepen/reconciliation/index-used-as-key">展示使用下标作为 key 时导致的问题</a>，以及<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/redirect-to-codepen/reconciliation/no-index-used-as-key">不使用下标作为 key 的例子的版本，修复了重新排列，排序，以及在列表头插入的问题</a>。</p>
<h4 id="权衡"><a href="#权衡" class="headerlink" title="权衡"></a>权衡</h4><p>​        我们定期优化启发式算法，让常见用例更高效地执行。在当前的实现中，可以理解为一棵子树能在其兄弟之间移动，但不能移动到其他位置。在这种情况下，算法会重新渲染整棵子树。</p>
<p>​        由于 React 依赖启发式算法，因此当以下假设没有得到满足，性能会有所损耗。</p>
<ol>
<li>该算法不会尝试匹配不同组件类型的子树。如果你发现你在两种不同类型的组件中切换，但输出非常相似的内容，建议把它们改成同一类型。在实践中，我们没有遇到这类问题。</li>
<li>Key 应该具有稳定，可预测，以及列表内唯一的特质。不稳定的 key（比如通过 <code>Math.random()</code> 生成的）会导致许多组件实例和 DOM 节点被不必要地重新创建，这可能导致性能下降和子组件中的状态丢失。</li>
</ol>
<h2 id="Refs-and-the-DOM"><a href="#Refs-and-the-DOM" class="headerlink" title="Refs and the DOM"></a>Refs and the DOM</h2><p>​        Refs 提供了一种方式，允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。</p>
<h3 id="回调Ref和普通ref的使用"><a href="#回调Ref和普通ref的使用" class="headerlink" title="回调Ref和普通ref的使用"></a>回调Ref和普通ref的使用</h3><h4 id="普通ref"><a href="#普通ref" class="headerlink" title="普通ref"></a>普通ref</h4><ul>
<li>需要使用 React.createRef(); 进行创建，否则不能ref成功</li>
<li>同时我们查看了 createRef 的方法里面实现了什么，发现就只是创建了一个 current 对象，然后再使用 Object.seal 将其对象进行了封闭操作。</li>
<li>同时如果我们创建一个对象。只要对象里面存在current属性，那么便可以存储ref</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">class Main extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    private inputRef:any</span><br><span class="line">    </span><br><span class="line">    constructor(props:any) &#123;</span><br><span class="line">        super(props);</span><br><span class="line">        </span><br><span class="line">        this.inputRef = React.createRef();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &lt;input type=&quot;text&quot; ref=&#123;this.inputRef&#125;/&gt;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="回调ref"><a href="#回调ref" class="headerlink" title="回调ref"></a>回调ref</h4><ul>
<li>不需要使用 React.createRef(); 进行初始化</li>
<li>通过使用回调函数，函数的第一个参数就是对应的DOM节点。</li>
<li>和普通ref一样使用ref属性attributes。</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">class Main extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    private inputRef:any</span><br><span class="line"></span><br><span class="line">    constructor(props:any) &#123;</span><br><span class="line">        super(props);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">        const setInputRef = (el:any) =&gt; &#123;</span><br><span class="line">            this.inputRef = el</span><br><span class="line">        &#125;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &lt;input type=&quot;text&quot; ref=&#123;setInputRef&#125;/&gt;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="关于回调-refs-的说明"><a href="#关于回调-refs-的说明" class="headerlink" title="关于回调 refs 的说明"></a>关于回调 refs 的说明</h4><p>​        如果 <code>ref</code> 回调函数是以内联函数的方式定义的，在更新过程中它会被执行两次，第一次传入参数 <code>null</code>，然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例，所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题，但是大多数情况下它是无关紧要的。</p>
<h2 id="Render-Props"><a href="#Render-Props" class="headerlink" title="Render Props"></a>Render Props</h2><p>​        术语 <a target="_blank" rel="noopener" href="https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce">“render prop”</a> 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术</p>
<h3 id="使用-Render-Props-来解决横切关注点（Cross-Cutting-Concerns）"><a href="#使用-Render-Props-来解决横切关注点（Cross-Cutting-Concerns）" class="headerlink" title="使用 Render Props 来解决横切关注点（Cross-Cutting Concerns）"></a>使用 Render Props 来解决横切关注点（Cross-Cutting Concerns）</h3><p>​        简单来说就是vue的插槽，vue的插槽，就是说，可以在使用这个组件的时候，自己定义组件内部的显示为自己定义的组件。然后同时还需要使用作用域插槽来解决作用于的问题。貌似</p>
<p>​        首先，我们知道可以使用 this.props.children 的属性可以有一个插槽的效果</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">class Main extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &#123;this.props.children&#125;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        但是，这样写，有一个作用域的问题，以及不能调用Main里面的属性。</p>
<p>​        所以此时就要使用 this.props.render 来代替 children 了</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">class Cat extends React.Component &#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    const mouse = this.props.mouse;</span><br><span class="line">    return (</span><br><span class="line">      &lt;img src=&quot;/cat.jpg&quot; style=&#123;&#123; position: &#x27;absolute&#x27;, left: mouse.x, top: mouse.y &#125;&#125; /&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Mouse extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    this.handleMouseMove = this.handleMouseMove.bind(this);</span><br><span class="line">    this.state = &#123; x: 0, y: 0 &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  handleMouseMove(event) &#123;</span><br><span class="line">    this.setState(&#123;</span><br><span class="line">      x: event.clientX,</span><br><span class="line">      y: event.clientY</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    return (</span><br><span class="line">      &lt;div style=&#123;&#123; height: &#x27;100vh&#x27; &#125;&#125; onMouseMove=&#123;this.handleMouseMove&#125;&gt;</span><br><span class="line"></span><br><span class="line">        &#123;/*</span><br><span class="line">          使用 `render`prop 动态决定要渲染的内容，</span><br><span class="line">          而不是给出一个 &lt;Mouse&gt; 渲染结果的静态表示</span><br><span class="line">        */&#125;</span><br><span class="line">        &#123;this.props.render(this.state)&#125;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class MouseTracker extends React.Component &#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    return (</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;h1&gt;移动鼠标!&lt;/h1&gt;</span><br><span class="line">        &lt;Mouse render=&#123;mouse =&gt; (</span><br><span class="line">          &lt;Cat mouse=&#123;mouse&#125; /&gt;</span><br><span class="line">        )&#125;/&gt;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        其思想就是，首先，this.props.render 这个是一个props属性，所以render是调用组件时传递过来的。然后传递这个参数this.state 这个参数就会作为 mouse，然后传递给Cat组件的mouse属性。</p>
<p>​        然后这个的返回值又会渲染。标签。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">this.props.render(this.state)</span><br><span class="line"></span><br><span class="line">Mouse render=&#123;mouse =&gt; (</span><br><span class="line">	Cat mouse=&#123;mouse&#125;	</span><br><span class="line">)&#125;</span><br></pre></td></tr></table></figure>

<p>​        关于 render prop 一个有趣的事情是你可以使用带有 render prop 的常规组件来实现大多数<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/higher-order-components.html">高阶组件</a> (HOC)。 例如，如果你更喜欢使用 <code>withMouse</code> HOC而不是 <code>&lt;Mouse&gt;</code> 组件，你可以使用带有 render prop 的常规 <code>&lt;Mouse&gt;</code> 轻松创建一个：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">// 如果你出于某种原因真的想要 HOC，那么你可以轻松实现</span><br><span class="line">// 使用具有 render prop 的普通组件创建一个！</span><br><span class="line">function withMouse(Component) &#123;</span><br><span class="line">  return class extends React.Component &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      return (</span><br><span class="line">        &lt;Mouse render=&#123;mouse =&gt; (</span><br><span class="line">          &lt;Component &#123;...this.props&#125; mouse=&#123;mouse&#125; /&gt;</span><br><span class="line">        )&#125;/&gt;</span><br><span class="line">      );</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        重要的是要记住，render prop 是因为模式才被称为 <em>render</em> prop ，你不一定要用名为 <code>render</code> 的 prop 来使用这种模式。事实上， <a target="_blank" rel="noopener" href="https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce"><em>任何</em>被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”</a>.</p>
<p>​        尽管之前的例子使用了 <code>render</code>，我们也可以简单地使用 <code>children</code> prop！</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;Mouse children=&#123;mouse =&gt; (</span><br><span class="line">  &lt;p&gt;鼠标的位置是 &#123;mouse.x&#125;，&#123;mouse.y&#125;&lt;/p&gt;</span><br><span class="line">)&#125;/&gt;</span><br></pre></td></tr></table></figure>

<p>​        记住，<code>children</code> prop 并不真正需要添加到 JSX 元素的 “attributes” 列表中。相反，你可以直接放置到元素的<em>内部</em>！</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;Mouse&gt;</span><br><span class="line">  &#123;mouse =&gt; (</span><br><span class="line">    &lt;p&gt;鼠标的位置是 &#123;mouse.x&#125;，&#123;mouse.y&#125;&lt;/p&gt;</span><br><span class="line">  )&#125;</span><br><span class="line">&lt;/Mouse&gt;</span><br></pre></td></tr></table></figure>

<p>​        这里，就是使用了 children 属性，我们可以知道，this.props.children 属性将作为组件的插槽形式进行传递。我们在调用组件的时候，直接作为内容，然后就会作为租价的children属性。</p>
<h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><h4 id="将-Render-Props-与-React-PureComponent-一起使用时要小心"><a href="#将-Render-Props-与-React-PureComponent-一起使用时要小心" class="headerlink" title="将 Render Props 与 React.PureComponent 一起使用时要小心"></a>将 Render Props 与 React.PureComponent 一起使用时要小心</h4><p>​        如果你在 render 方法里创建函数，那么使用 render prop 会抵消使用 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-api.html#reactpurecomponent"><code>React.PureComponent</code></a> 带来的优势。因为浅比较 props 的时候总会得到 false，并且在这种情况下每一个 <code>render</code> 对于 render prop 将会生成一个新的值。</p>
<h2 id="静态类型检查"><a href="#静态类型检查" class="headerlink" title="静态类型检查"></a>静态类型检查</h2><p>​        像 <a target="_blank" rel="noopener" href="https://flow.org/">Flow</a> 和 <a target="_blank" rel="noopener" href="https://www.typescriptlang.org/">TypeScript</a> 等这些静态类型检查器，可以在运行前识别某些类型的问题。他们还可以通过增加自动补全等功能来改善开发者的工作流程。出于这个原因，我们建议在大型代码库中使用 Flow 或 TypeScript 来代替 <code>PropTypes</code>。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx create-react-app my-app --template typescript</span><br></pre></td></tr></table></figure>

<p>​        如需将 TypeScript 添加到<strong>现有的 Create React App 项目</strong>中，<a target="_blank" rel="noopener" href="https://facebook.github.io/create-react-app/docs/adding-typescript">请参考此文档</a>.</p>
<h3 id="添加-TypeScript-到现有项目中"><a href="#添加-TypeScript-到现有项目中" class="headerlink" title="添加 TypeScript 到现有项目中"></a>添加 TypeScript 到现有项目中</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://zh-hans.reactjs.org/docs/static-type-checking.html#adding-typescript-to-a-project</span><br></pre></td></tr></table></figure>

<ul>
<li>先下载typescript的安装包</li>
<li>然后在package.json 里面添加scripts指令。tsc就是将ts编译的指令。</li>
<li>创建tsconfig.json 的文件，用于typescript的编译配置<ul>
<li>可以使用 npm tsc –init</li>
<li><code>tsconfig.json</code> 文件中，有许多配置项用于配置编译器。查看所有配置项的的详细说明，<a target="_blank" rel="noopener" href="https://www.typescriptlang.org/docs/handbook/tsconfig-json.html">请参考此文档</a>。</li>
</ul>
</li>
</ul>
<h2 id="严格模式"><a href="#严格模式" class="headerlink" title="严格模式"></a>严格模式</h2><p>​        <code>StrictMode</code> 是一个用来突出显示应用程序中潜在问题的工具。与 <code>Fragment</code> 一样，<code>StrictMode</code> 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。</p>
<blockquote>
<p>注意：</p>
<p>严格模式检查仅在开发模式下运行；<em>它们不会影响生产构建</em>。</p>
</blockquote>
<p>​        你可以为应用程序的任何部分启用严格模式。例如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">function ExampleApplication() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Header /&gt;</span><br><span class="line">      &lt;React.StrictMode&gt;        &lt;div&gt;</span><br><span class="line">          &lt;ComponentOne /&gt;</span><br><span class="line">          &lt;ComponentTwo /&gt;</span><br><span class="line">        &lt;/div&gt;</span><br><span class="line">      &lt;/React.StrictMode&gt;      &lt;Footer /&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        在上述的示例中，<em>不</em>会对 <code>Header</code> 和 <code>Footer</code> 组件运行严格模式检查。但是，<code>ComponentOne</code> 和 <code>ComponentTwo</code> 以及它们的所有后代元素都将进行检查。</p>
<p>​        <code>StrictMode</code> 目前有助于：</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#identifying-unsafe-lifecycles">识别不安全的生命周期</a></li>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#warning-about-legacy-string-ref-api-usage">关于使用过时字符串 ref API 的警告</a></li>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage">关于使用废弃的 findDOMNode 方法的警告</a></li>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects">检测意外的副作用</a></li>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#detecting-legacy-context-api">检测过时的 context API</a></li>
</ul>
<p>​        未来的 React 版本将添加更多额外功能。</p>
<h2 id="使用-PropTypes-进行类型检查"><a href="#使用-PropTypes-进行类型检查" class="headerlink" title="使用 PropTypes 进行类型检查"></a>使用 PropTypes 进行类型检查</h2><blockquote>
<p>注意：</p>
<p>自 React v15.5 起，<code>React.PropTypes</code> 已移入另一个包中。请使用 <a target="_blank" rel="noopener" href="https://www.npmjs.com/package/prop-types"><code>prop-types</code> 库</a> 代替。</p>
<p>我们提供了一个 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactproptypes">codemod 脚本</a>来做自动转换。</p>
</blockquote>
<h2 id="非受控组件"><a href="#非受控组件" class="headerlink" title="非受控组件"></a>非受控组件</h2><p>​        在大多数情况下，我们推荐使用 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/forms.html#controlled-components">受控组件</a> 来处理表单数据。在一个受控组件中，表单数据是由 React 组件来管理的。另一种替代方案是使用非受控组件，这时表单数据将交由 DOM 节点来处理。</p>
<h3 id="默认值"><a href="#默认值" class="headerlink" title="默认值"></a>默认值</h3><p>​        在 React 渲染生命周期时，表单元素上的 <code>value</code> 将会覆盖 DOM 节点中的值。在非受控组件中，你经常希望 React 能赋予组件一个初始值，但是不去控制后续的更新。 在这种情况下, 你可以指定一个 <code>defaultValue</code> 属性，而不是 <code>value</code>。在一个组件已经挂载之后去更新 <code>defaultValue</code> 属性的值，不会造成 DOM 上值的任何更新。</p>
<h2 id="Web-Components"><a href="#Web-Components" class="headerlink" title="Web Components"></a>Web Components</h2><p>​        React 和 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/Web_Components">Web Components</a> 为了解决不同的问题而生。Web Components 为可复用组件提供了强大的封装，而 React 则提供了声明式的解决方案，使 DOM 与数据保持同步。两者旨在互补。作为开发人员，可以自由选择在 Web Components 中使用 React，或者在 React 中使用 Web Components，或者两者共存。</p>

    </div>

    
    
    

    <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/React/" rel="tag"># React</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2022/02/15/JavaScript/JavaScript-%E7%B1%BB/" rel="prev" title="JavaScript-类">
                  <i class="fa fa-chevron-left"></i> JavaScript-类
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2022/02/18/React/React-Hook/" rel="next" title="React-Hook">
                  React-Hook <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">tallgy</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  





  





</body>
</html>
